/*! JRoll v1.0.8 ~ (c) 2015 Author:jlong, Email:jlong@chjtx.com Website:http://www.chjtx.com/JRoll/ */
;(function (window, document, Math) {
  "use strict";
  var rAF = window.requestAnimationFrame || window.webkitRequestAnimationFrame || function(callback) {
    setTimeout(callback, 16);
  };
  var sty = document.createElement('div').style;
  var tSF = ("transform" in sty) ? "transform" : "-webkit-transform";
  var tSD = ("transitionDuration" in sty) ? "transition-duration" : "-webkit-transition-duration";
  var isMobile = /(iPhone)|(Android)|(iPad)/.test(navigator.userAgent);
  var isAndroid = /Android/.test(navigator.userAgent);
  var focusEl = null; //用于暂存focus的表单元素，在android收回软键盘时使其blur
  var normalImport = function(d){ //强制普通模式引入，script添加"jroll-import='normal'"属性后将忽略exports/define等方式引入
    var s = d.getElementsByTagName("script");
    for (var i=s.length-1; i>-1; i--) {
      if (s[i].getAttribute("jroll-import") === 'normal') {
        return true;
      }
    }
    return false;
  }(document);

  //计算相对位置，a相对于b的位置
  function computePosition(a, b) {
    var left = 0,
        top = 0;
    while (a) {
      left += a.offsetLeft;
      top += a.offsetTop;
      a = a.offsetParent;
      if (a === b) {
        a = null;
      }
    }
    return {
      left : left,
      top : top
    };
  }

  function JRoll(el, options) {
    var me = this;
    me.wrapper = typeof el === 'string' ? document.querySelector(el) : el;
    me.scroller = options && options.scroller ? (typeof options.scroller === 'string' ? document.querySelector(options.scroller) : options.scroller) : me.wrapper.children[0];
    
    //计算wrapper相对document的位置
    me.wrapperOffset = computePosition(me.wrapper, document.body);

    //默认选项
    me.options = {
      scroll : true, //使能滑动
      scrollX : true,
      scrollY : true,
      scrollFree : false, //自由滑动
      zoom : false, //使能缩放
      zoomMin : 1, //最小缩放倍数
      zoomMax : 4, //最大缩放倍数
      bounce : false, //回弹
      scrollBarX : false, //开启x滚动条
      scrollBarY : false, //开启y滚动条
      scrollBarFade : false, //滚动条使用渐隐模式
      stopPropagation : false, //禁止冒泡
      preventDefault : true, //禁止touchmove默认事件
      momentum : true, //滑动结束平滑过渡
      autoStyle: true, //自动为wrapper和scroller添加样式
      g : 0.0008, //模拟重力加速度,g值越小，运动时间越长
      adjustTop: 190 //安卓手机输入表单时自动调整输入框位置
    };

    for (var i in options) {
      if (i !== "scroller") {
        me.options[i] = options[i];
      }
    }

    if (me.options.autoStyle) {
      //将wrapper设为relative
      if (window.getComputedStyle(me.wrapper).position === "static") {
        me.wrapper.style.position = "relative";
      }
      me.wrapper.style.overflow = "hidden";
      me.scroller.style.minHeight = "100%";
    }

    me.x = 0;
    me.y = 0;
    me.status = null; //手势状态，null | 'start' | 'end' | 'scroll' | 'zoom'
    me.instance = null; //滑动实例
    me.scrollBarX = null; //x滚动条
    me.scrollBarY = null; //y滚动条

    me._scroll = {
      startX : 0,
      startY : 0,
      endX : 0,
      endY : 0,
      lockX : false,
      lockY : false
    };

    me._zoom = {
      spacing : 0, //两指间间距
      scale : 1,
      startScale : 1
    };

    me._event = {
      "scrollStart" : [],
      "scroll" : [],
      "scrollEnd" : [],
      "zoomStart" : [],
      "zoom" : [],
      "zoomEnd" : []
    };

    //添加scrollStart默认事件
    me._event.scrollStart.push(me._defaultScrollStart);
    //添加scroll默认事件
    me._event.scroll.push(me._defaultScroll);
    //添加scrollEnd默认事件
    me._event.scrollEnd.push(me._defaultScrollEnd);

    me.refresh();

    if (isMobile) {
      me.scroller.addEventListener("touchstart", me._touch.bind(me, me.scroller), false);
      document.addEventListener("touchmove", me._touch.bind(me, me.scroller), false);
      document.addEventListener("touchend", me._touch.bind(me, me.scroller), false);
    } else {
      me.scroller.addEventListener("mousedown", me._touch.bind(me, me.scroller), false);
      document.addEventListener("mousemove", me._touch.bind(me, me.scroller), false);
      document.addEventListener("mouseup", me._touch.bind(me, me.scroller), false);
      //监听鼠标滚轮
      me.scroller.addEventListener("mousewheel", me._wheel.bind(me), false);
    }
    //监听表单事件，以调整窗口变化
    if (isAndroid) {
      me.scroller.addEventListener("focusin", me._focusin.bind(me), false);
      me.scroller.addEventListener("focusout", me._focusout.bind(me), false);
      window.addEventListener("resize", me._resize.bind(me), false);
    }
  }

  JRoll.prototype = {
    refresh : function () {
      var me = this, temp;
      me.wrapperWidth = me.wrapper.clientWidth;
      me.wrapperHeight = me.wrapper.clientHeight;

      me.scrollerWidth = Math.round(me.scroller.offsetWidth * me._zoom.scale);
      me.scrollerHeight = Math.round(me.scroller.offsetHeight * me._zoom.scale);

      me.maxScrollX = me.wrapperWidth - me.scrollerWidth;
      me.maxScrollY = me.wrapperHeight - me.scrollerHeight;

      if (me.maxScrollX >= 0) {
        if (!me.options.bounce) {
          me._scroll.lockX = true;
        }
        me.maxScrollX = 0;
      } else {
        me._scroll.lockX = false;
      }
      if (me.maxScrollY >= 0) {
        if (!me.options.bounce) {
          me._scroll.lockY = true;
        }
        me.maxScrollY = 0;
      } else {
        me._scroll.lockY = false;
      }

      me._scroll.endX = me.x;
      me._scroll.endY = me.y;

      //x滚动条
      if (me.options.scrollBarX) {
        if (!me.scrollBarX) {
          temp = me._createScrollBar("JRoll-xbar", "JRoll-xbtn", false);
          me.scrollBarX = temp[0];
          me.scrollBtnX = temp[1];
        }
        me.scrollBarScaleX = me.wrapper.clientWidth / me.scrollerWidth;
        me.scrollBtnX.style.width = Math.round(me.scrollBarX.clientWidth * me.scrollBarScaleX) + "px";
      } else if (me.scrollBarX) {
        me.wrapper.removeChild(me.scrollBarX);
        me.scrollBarX = null;
      }
      //y滚动条
      if (me.options.scrollBarY) {
        if (!me.scrollBarY) {
          temp = me._createScrollBar("JRoll-ybar", "JRoll-ybtn", true);
          me.scrollBarY = temp[0];
          me.scrollBtnY = temp[1];
        }
        me.scrollBarScaleY = me.wrapper.clientHeight / me.scrollerHeight;
        me.scrollBtnY.style.height = Math.round(me.scrollBarY.clientHeight * me.scrollBarScaleY) + "px";
      } else if (me.scrollBarY) {
        me.wrapper.removeChild(me.scrollBarY);
        me.scrollBarY = null;
      }

      return me;
    },

    _focusin: function(e) {
      var me = this;
      focusEl = e.target;
      setTimeout(function() {
        var pos, m;
        me.refresh();
        pos = computePosition(e.target, me.wrapper);
        m = pos.top + me.y;
        if (m > me.options.adjustTop) {
          me.scrollTo(me.x, me.y - m + me.options.adjustTop, 400);
        }
      }, 600);
    },

    _focusout: function(e) {
      var me = this;
      setTimeout(function() {
        me.refresh();
        me.scrollTo(me.x, me.y, 400);
      }, 600);  //android有些比较迟钝的浏览器软键盘收起需要600ms
    },

    _resize: function() {
      var me = this;
      var x = me.maxScrollX, y = me.maxScrollY;
      setTimeout(function() {
        me.refresh();
        if (x !== me.maxScrollX || y !== me.maxScrollY) {
          if (x < me.maxScrollX || y < me.maxScrollY) {
            if (focusEl) {
              focusEl.blur();
            }
          }
          me.scrollTo(me.x, me.y, 400);
        }
      }, 600);
    },

    _scrollTo : function (x, y) {
      this.scroller.style[tSF] = "translate(" + x + "px, " + y + "px) translateZ(0) scale(" + this._zoom.scale + ")";
    },

    //供用户调用的scrollTo方法
    scrollTo : function(x, y, timing) {
      var me = this;
      me._scroll.endX = me.x = (x >= 0) ? 0 : (x <= me.maxScrollX) ? me.maxScrollX : x;
      me._scroll.endY = me.y = (y >= 0) ? 0 : (y <= me.maxScrollY) ? me.maxScrollY : y;
      if (timing) {
        me.scroller.style[tSD] = timing + "ms";
        setTimeout(function(){
          me.scroller.style[tSD] = "0ms";
        }, timing);
      }
      me._scrollTo(me.x, me.y);
      me._runScrollBar();

      return me;
    },

    _wheel: function(e) {
      var me = this;
      me.scrollTo(me.x, me._compute(me.y + e.wheelDelta, me.maxScrollY));
      me._runScrollBar();
    },

    on : function (event, callback) {
      var me = this;
      switch (event) {
        case "scrollStart":
          me._event.scrollStart.push(callback);
          break;
        case "scroll":
          me._event.scroll.push(callback);
          break;
        case "scrollEnd":
          me._event.scrollEnd.push(callback);
          break;
        case "zoomStart":
          me._event.zoomStart.push(callback);
          break;
        case "zoom":
          me._event.zoom.push(callback);
          break;
        case "zoomEnd":
          me._event.zoomEnd.push(callback);
          break;
      }
    },

    _execEvent : function (event) {
      var me = this;
      for (var i = 0, l = me._event[event].length; i < l; i++) {
        me._event[event][i].call(me);
      }
    },

    //创建滚动条
    _createScrollBar : function (a, b, isY) {
      var me = this;
      var bar, btn, height;

      bar = document.createElement("div");
      btn = document.createElement("div");
      bar.className = a;
      btn.className = b;

      if (this.options.scrollBarX === true || this.options.scrollBarY === true) {
        if (isY) {
          bar.style.cssText = "background:#999;position:absolute;top:0;right:0;bottom:0;width:3px;height:100%;overflow:hidden;";
          btn.style.cssText = "background:#000;position:absolute;top:0;left:0;right:0;";
        } else {
          bar.style.cssText = "background:#999;position:absolute;left:0;bottom:0;right:0;height:3px;width:100%;overflow:hidden;";
          btn.style.cssText = "background:#000;height:100%;position:absolute;left:0;top:0;bottom:0;";
        }
      }
      
      if (me.options.scrollBarFade) {
        bar.style.opacity = 0;
      }

      bar.appendChild(btn);
      me.wrapper.appendChild(bar);

      return [bar, btn];
    },

    //滑动滚动条
    _runScrollBar : function () {
      var me = this;

      if (!me._scroll.lockX && me.scrollBtnX) {
        var x = Math.round(-me.x * me.scrollBarScaleX);
        me._scrollTo.call({
          scroller : me.scrollBtnX,
          _zoom : {
            scale : 1
          }
        }, x, 0);
      }
      
      if (!me._scroll.lockY && me.scrollBtnY) {
        var y = Math.round(-me.y * me.scrollBarScaleY);
        me._scrollTo.call({
          scroller : me.scrollBtnY,
          _zoom : {
            scale : 1
          }
        }, 0, y);
      }
    },

    //回弹效果
    _bounceFun : function (k, t, a, isY) {
      var me = this;
      var s;
      t = t - 16;
      if (t > 0) {
        s = a + k * t * t;
      } else {
        if (isY) {
          me._scroll.endY = me.y = a;
          me._scrollTo(me.x, a);
        } else {
          me._scroll.endX = me.x = a;
          me._scrollTo(a, me.y);
        }
        me._execEvent("scrollEnd");
        return;
      }
      if (isY) {
        me._scroll.endY = me.y = s;
        me._scrollTo(me.x, s);
      } else {
        me._scroll.endX = me.x = s;
        me._scrollTo(s, me.y);
      }
      me._execEvent("scroll");
      rAF(me._bounceFun.bind(me, k, t, a, isY));
    },

    //scrollStart默认事件
    _defaultScrollStart : function () {
      var me = this;
      if (me.options.scrollBarFade) {
        me.scrollBarX && (me.scrollBarX.style.opacity = 1);
        me.scrollBarY && (me.scrollBarY.style.opacity = 1);
      }
    },

    //scroll默认事件
    _defaultScroll : function () {
      //滚动条
      this._runScrollBar();
    },

    //回弹
    _bounceEvent : function() {
      var me = this;
      var tempX, tempY, k, t = 200;
      tempX = me.x >= 0 ? 0 : (me.x <= me.maxScrollX ? me.maxScrollX : me.x);
      tempY = me.y >= 0 ? 0 : (me.y <= me.maxScrollY ? me.maxScrollY : me.y);

      if (tempX !== me.x) {
        k = (me.x - tempX) / 40000;
        me._bounceFun(k, t, tempX, false);
      }

      if (tempY !== me.y) {
        k = (me.y - tempY) / 40000;
        me._bounceFun(k, t, tempY, true);
      }

      if (tempX === me.x && tempY === me.y) {
        me._execEvent("scrollEnd");
      }
    },

    //scrollEnd默认事件
    _defaultScrollEnd : function () {
      var me = this;
      //滚动条
      me._runScrollBar();
      if (me.options.scrollBarFade) {
        me.scrollBarX && me._fade(me.scrollBarX, 2000);
        me.scrollBarY && me._fade(me.scrollBarY, 2000);
      }
    },

    //滚动条渐隐
    _fade : function (bar, time) {
      var me = this;
      if ((me.status === "end" || me.status === "start") && time > 0) {
        time = time - 25;
        (time % 100 === 0) && (bar.style.opacity = time / 1000);
      } else {
        return;
      }
      rAF(me._fade.bind(me, bar, time));
    },

    //滑动结束后执行的动作
    _endAction : function (tag) {
      var me = this;
      me._scroll.lockX = false;
      me._scroll.lockY = false;
      if (!tag) {
        me.instance = null;
      }
      me.status = tag || "end";
      if (me.options.bounce) {
        me._bounceEvent();
      } else {
        me._execEvent("scrollEnd");
      }
    },

    _step : function () {
      var me = this;
      var tempX, tempY;

      if (me.status !== "scroll") {
        if (me.status !== "start") {
          me._endAction();
        } else {
          me._endAction("start");
        }
        return;
      }

      if (me.options.scrollFree && me.timeX <= 0 && me.timeY <= 0) {
        me._endAction();
        return;
      }

      //处理x方向
      if (!me._scroll.lockX) {
        if (me.timeX <= 0 && !me.options.scrollFree) {
          me._endAction();
          return;
        }

        if (me.timeX <= 0 && me.options.scrollFree) {
          me.speedX = 0;
        } else {
          me.speedX = +(me.directionX + me.timeX * me.options.g);
        }
        me.x = me._scroll.endX = Math.round(me._scroll.endX + me.speedX * 16);
        tempX = me._compute(me.x, me.maxScrollX);
        if (tempX !== me.x) {
          me.x = me._scroll.endX = (me.options.bounce && (me.x - tempX > 10) ? parseInt(me.directionX + 25, 10) + tempX : tempX);
          if (!me.options.scrollFree) {
            me.status = "end";
          }
        }
        me.timeX = me.timeX - 16;
      }

      //处理y方向
      if (!me._scroll.lockY) {
        if (me.timeY <= 0 && !me.options.scrollFree) {
          me._endAction();
          return;
        }

        if (me.timeY <= 0 && me.options.scrollFree) {
          me.speedY = 0;
        } else {
          me.speedY = +(me.directionY + me.timeY * me.options.g);
        }
        me.y = me._scroll.endY = Math.round(me._scroll.endY + me.speedY * 16);
        tempY = me._compute(me.y, me.maxScrollY);
        if (tempY !== me.y) {
          me.y = me._scroll.endY = (me.options.bounce && (me.y - tempY > 10) ? parseInt(me.directionY + 25, 10) + tempY : tempY);
          if (!me.options.scrollFree) {
            me.status = "end";
          }
        }
        me.timeY = me.timeY - 16;
      }

      me._scrollTo(me.x, me.y);
      me._execEvent("scroll");

      rAF(me._step.bind(me));
    },

    //计算x,y的值
    _compute : function (val, max) {
      var me = this;
      if (val > 0) {
        if (me.options.bounce && (val > 10)) {
          return Math.round(val / 5);
        } else {
          return 0;
        }
      }

      if (val < max) {
        if (me.options.bounce && (val < (max - 10))) {
          return Math.round(max + ((val - max) / 5));
        } else {
          return max;
        }
      }

      return val;
    },

    //触发事件
    _touch : function (el, e) {
      var me = this;
      var type = e.type,
      t = e.touches || [e];

      if (me.options.stopPropagation) {
        e.stopPropagation();
      }

      switch (type) {
      case "touchstart":
      case "mousedown":
        me.status = "start";
        me.instance = el;

        if (t.length === 1) { //单指
          me.startTime = Date.now();
          me.startPositionX = me._scroll.startX = t[0].pageX;
          me.startPositionY = me._scroll.startY = t[0].pageY;
        } else { //双指
          me.scroller.style["-webkit-transform-origin"] = "0 0";

          var c1 = Math.abs(t[0].pageX - t[1].pageX),
          c2 = Math.abs(t[0].pageY - t[1].pageY);

          me._zoom.spacing = Math.sqrt(c1 * c1 + c2 * c2);
          me._zoom.startScale = me._zoom.scale;

          me.originX = Math.abs(t[0].pageX + t[1].pageX) / 2 - me.wrapperOffset.left - me.x;
          me.originY = Math.abs(t[0].pageY + t[1].pageY) / 2 - me.wrapperOffset.top - me.y;
        }

        break;
      case "touchmove":
      case "mousemove":
        if (!me.instance) {
          return;
        } else {
          if (me.options.preventDefault) {
            e.preventDefault();
          }
        }

        //判断手势
        if (me.status === "start") {
          if (t.length === 1 && me.options.scroll) {
            //判断向哪个方向滑动，如果已有一个方向被锁定则不用判断
            if (!me.options.scrollFree && !(me._scroll.lockX || me._scroll.lockY)) {
              if (Math.abs(t[0].pageX - me._scroll.startX) > Math.abs(t[0].pageY - me._scroll.startY)) {
                me._scroll.lockY = true; //不允许y方向滑动
              } else {
                me._scroll.lockX = true; //不允许x方向滑动
              }
            }
            if (!me.options.scrollX) {
              me._scroll.lockX = true;
            }
            if (!me.options.scrollY) {
              me._scroll.lockY = true;
            }
            me._execEvent("scrollStart");
            me.status = "scroll";
          } else if (me.options.zoom) {
            me._execEvent("zoomStart");
            me.status = "zoom";
          }
          return;
        }

        //执行滑动
        if (me.status === "scroll") {
          if (!me._scroll.lockX) {
            me.x = t[0].pageX - me._scroll.startX + me._scroll.endX;
            me.x = me._compute(me.x, me.maxScrollX);
          }
          if (!me._scroll.lockY) {
            me.y = t[0].pageY - me._scroll.startY + me._scroll.endY;
            me.y = me._compute(me.y, me.maxScrollY);
          }

          me._scrollTo(me.x, me.y);
          me._execEvent("scroll");

          if (Date.now() - me.startTime > 200) {
            me.startTime = Date.now() - 50;
            me.startPositionX = t[0].pageX;
            me.startPositionY = t[0].pageY;
          }
          if (!me._scroll.lockX) {
            me.distanceX = t[0].pageX - me.startPositionX;
          }
          if (!me._scroll.lockY) {
            me.distanceY = t[0].pageY - me.startPositionY;
          }

          return;
        }

        //执行缩放
        if (me.status === "zoom") {
          var c1 = Math.abs(t[0].pageX - t[1].pageX),
          c2 = Math.abs(t[0].pageY - t[1].pageY),
          spacing = Math.sqrt(c1 * c1 + c2 * c2),
          scale = spacing / me._zoom.spacing * me._zoom.startScale,
          lastScale;

          if (scale < me.options.zoomMin) {
            scale = me.options.zoomMin;
          } else if (scale > me.options.zoomMax) {
            scale = me.options.zoomMax;
          }

          lastScale = scale / me._zoom.startScale;

          me.x = Math.round(me.originX - me.originX * lastScale + me._scroll.endX);
          me.y = Math.round(me.originY - me.originY * lastScale + me._scroll.endY);
          me._zoom.scale = scale;

          me._scrollTo(me.x, me.y);
          me._execEvent("zoom");

          return;
        }

        break;
      case "touchend":
      case "mouseup":
        //释放鼠标
        if (me.status === "start") {
          me.instance = null;
          me.status = "end";
        }
        //滑动结束
        if (me.status === "scroll") {
          me._scroll.endX = me.x;
          me._scroll.endY = me.y;

          if (me.options.momentum) {
            var runX = false,
            runY = false;
            me.duration = Date.now() - me.startTime;

            if (me.duration < 300) {
              if (!me._scroll.lockX) {
                me.speedX = me.distanceX / me.duration;
                me.timeX = Math.abs(me.speedX / me.options.g);
                if (me.speedX < 0) {
                  me.directionX = "-";
                } else {
                  me.directionX = "+";
                }

                runX = true;
              }

              if (!me._scroll.lockY) {
                me.speedY = me.distanceY / me.duration;
                me.timeY = Math.abs(me.speedY / me.options.g);
                if (me.speedY < 0) {
                  me.directionY = "-";
                } else {
                  me.directionY = "+";
                }

                runY = true;
              }

              if (runX || runY) {
                rAF(me._step.bind(me));
              } else {
                me._endAction();
              }
            } else {
              me._endAction();
            }
          } else {
            me._endAction();
          }

          return;
        }

        //缩放结束
        if (me.status === "zoom") {

          if (me._zoom.scale > me.options.zoomMax) {
            me._zoom.scale = me.options.zoomMax;
          } else if (me._zoom.scale < me.options.zoomMin) {
            me._zoom.scale = me.options.zoomMin;
          }

          me.refresh();

          if (me.x > 0) {
            me._scroll.endX = me.x = 0;
          } else if (me.x < me.maxScrollX) {
            me._scroll.endX = me.x = me.maxScrollX;
          }
          if (me.y > 0) {
            me._scroll.endY = me.y = 0;
          } else if (me.y < me.maxScrollY) {
            me._scroll.endY = me.y = me.maxScrollY;
          }

          me.scrollTo(me.x, me.y, 400);

          me.status = "end";
          me._execEvent("zoomEnd");

          return;
        }

        break;
      }
    }
  };

  if (typeof module !== 'undefined' && module.exports && !normalImport) {
    module.exports = JRoll;
  } else if (typeof define === 'function' && !normalImport) {
    define(function() {return JRoll});
  } else {
    window.JRoll = JRoll;
  }

})(window, document, Math);
